package com.zhl.common.utils;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.util.Map;

/**
 * @description: spring容器操作工具类
 * @author: duanwei
 * @create: 2020-05-28 13:57
 **/
@Component
public class SpringUtils implements ApplicationContextAware {
    private static ApplicationContext applicationContext;

    private static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 利用aware注入application
     *
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // 注入application
        SpringUtils.applicationContext = applicationContext;
    }

    /**
     * 通过name获取bean
     *
     * @param name
     * @return
     */
    @SuppressWarnings("unchecked")
    public static Object getBean(String name) {
        assertApplicationContext(name, null);
        return getApplicationContext().getBean(name);
    }

    /**
     * 通过class获取bean
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> clazz) {
        return getApplicationContext().getBean(clazz);
    }

    /**
     * 通过name和class获取bean
     *
     * @param name
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(String name, Class<T> clazz) {
        return getApplicationContext().getBean(name, clazz);
    }

    /**
     * 根据clazz类型获取spring容器中的对象
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> Map<String, T> getBeansOfType(Class<T> clazz) {
        return getApplicationContext().getBeansOfType(clazz);
    }

    /**
     * 根据注解类从容器中获取对象
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> Map<String, Object> getBeansOfAnnotation(Class<? extends Annotation> clazz) {
        return getApplicationContext().getBeansWithAnnotation(clazz);
    }

    private static void assertApplicationContext(String beanName, Class requiredType) {
        if (SpringUtils.applicationContext == null) {
            throw new RuntimeException("ApplicaitonContext property is NULL, please check whether SpringContextHolder is injected!");
        }
        if (beanName != null) {
            boolean have = applicationContext.containsBean(beanName);
            if (!have) {
                throw new RuntimeException("This bean is not managed by the Spring container  ");
            }
        }
        if (requiredType != null) {
            String[] beanNamesForType = applicationContext.getBeanNamesForType(requiredType);
            if (beanNamesForType == null || beanNamesForType.length == 0) {
                throw new RuntimeException("This bean is not managed by the Spring container  ");
            }
            if (beanNamesForType.length != 1) {
                throw new RuntimeException("Class type fetching is not supported for multiple types of beans. Use the name to get this bean");
            }
            boolean have = applicationContext.containsBean(beanNamesForType[0]);
            if (!have) {
                throw new RuntimeException("This bean is not managed by the Spring container ");
            }
        }

    }
}
